package com.nononsenseapps.feeder.processor

import android.app.Application
import cn.ekuma.odbo.mobile.cache.EntityCacheManager
import cn.ekuma.odbo.mobile.cache.IEntityCacheManager
import com.googlecode.aviator.AviatorEvaluator
import com.googlecode.aviator.script.AviatorScriptEngine
import com.nononsenseapps.feeder.db.entity.FEED_ITEM_RULE_ACTION
import com.nononsenseapps.feeder.db.entity.FEED_ITEM_RULE_SOURCE
import com.nononsenseapps.feeder.db.entity.FeedItem
import com.nononsenseapps.feeder.db.entity.FeedRule
import com.nononsenseapps.feeder.db.room.*
import timber.log.Timber
import java.lang.Exception

class RuleManager(private val feedRuleDao: FeedRuleDao,private val feedItemDao: FeedItemDao) {

    private val ruleCacheManager: IEntityCacheManager<Long,List<FeedRule>> = EntityCacheManager{ feedId->
        when(feedId) {
        ID_ALL_FEEDS->feedRuleDao.listGlobal()
        else->feedRuleDao.listWithFeed(feedId)
    }
    }

    val aviatorScriptEngine by lazy { AviatorEvaluator.getInstance()}


    fun clearCache(){
        ruleCacheManager.clearCache()
    }

    fun feedRuleChanged(feedId: Long){
       ruleCacheManager.valueChanged(feedId)
    }

    companion object{
        var  INSTANCE:RuleManager?=null

        fun init(application: Application){
            val db=AppDatabase.getInstance(application)
            init(db.feedRuleDao(),db.feedItemDao())
        }

        fun init(feedRuleDao: FeedRuleDao,feedItemDao: FeedItemDao){
            INSTANCE?:synchronized(this) {
                INSTANCE?: RuleManager(feedRuleDao, feedItemDao).let { INSTANCE=it }
            }
        }
    }

    fun itemRuleCheck(feedItem: FeedItem) :Boolean{
        var modifyFlag = false
        feedItem.feedId?.let {feedId->
            ruleCacheManager.getValue(feedId)?.forEach {
                modifyFlag = modifyFlag || ruleChecking(it,feedItem)
            }
        }

        ruleCacheManager.getValue(ID_ALL_FEEDS)?.forEach {
            modifyFlag = modifyFlag || ruleChecking(it,feedItem)
        }
        return modifyFlag
   }

    private fun ruleChecking(rule: FeedRule, feedItem: FeedItem):Boolean{
        if(!match(rule,feedItem)) return false
        Timber.d("check rule action with text:${rule.ruleValue} \t action type:${rule.actionType} \t  type: ${rule.style} ")
        when(rule.actionType){
            FEED_ITEM_RULE_ACTION.MOVE_FEED-> {
                feedItem.feedId = rule.actionFeedId
            }
            FEED_ITEM_RULE_ACTION.REPLACE->{
                when(rule.style){
                    FEED_ITEM_RULE_SOURCE.TITLE ->feedItem.plainTitle = ruleReplace(feedItem.plainTitle,rule)
                    FEED_ITEM_RULE_SOURCE.IMAGE_URL -> {
                        feedItem.imageUrl?.let{
                            feedItem.imageUrl = ruleReplace(it, rule)
                        }
                        if(rule.adClick)
                            feedItem.description = ruleReplace(feedItem.description , rule)
                    }
                    FEED_ITEM_RULE_SOURCE.AUTHOR ->feedItem.author=ruleReplace(feedItem.author!!,rule)
                    FEED_ITEM_RULE_SOURCE.DESCRIPTION ->feedItem.description=ruleReplace(feedItem.description,rule)
                    FEED_ITEM_RULE_SOURCE.ENCLOSURE_LINK ->feedItem.enclosureLink=ruleReplace(feedItem.enclosureLink!!,rule)
                    FEED_ITEM_RULE_SOURCE.ITEM_TAGS ->feedItem.itemTags=ruleReplace(feedItem.itemTags,rule)
                    FEED_ITEM_RULE_SOURCE.LINK ->feedItem.link=ruleReplace(feedItem.link!!,rule)
                    else ->{return false}
                }
            }
            FEED_ITEM_RULE_ACTION.MARK_READ->{
                feedItem.unread=false
            }
            FEED_ITEM_RULE_ACTION.SCRIPT -> {
                val mapEnv = mutableMapOf("feedItem" to feedItem)
                try {
                    val script =aviatorScriptEngine.compile(rule.id.toString(), rule.ruleValue, true)
                    val changeFlag = script.execute(mapEnv as Map<String, Any>)
                    changeFlag?.let {
                        if (it as Boolean) return it
                    }
                } catch (e:Exception) {
                    return false
                }
                return false
            }
            else->return false
        }
        return true
    }

    private fun match(rule: FeedRule, feedItem: FeedItem):Boolean{
        val checkConstant:String? = when(rule.style){
            FEED_ITEM_RULE_SOURCE.TITLE ->feedItem.plainTitle
            FEED_ITEM_RULE_SOURCE.IMAGE_URL ->feedItem.imageUrl
            FEED_ITEM_RULE_SOURCE.AUTHOR ->feedItem.author
            FEED_ITEM_RULE_SOURCE.DESCRIPTION ->feedItem.description
            FEED_ITEM_RULE_SOURCE.ENCLOSURE_LINK ->feedItem.enclosureLink
            FEED_ITEM_RULE_SOURCE.ITEM_TAGS ->feedItem.itemTags
            FEED_ITEM_RULE_SOURCE.LINK ->feedItem.link
            else ->null
        }

        Timber.d("check rule match with text:${rule.ruleValue} \t type: ${rule.style} \tcheckConstant:$checkConstant")
        if(checkConstant.isNullOrEmpty() && !rule.adClick) return false

        if(rule.actionType == FEED_ITEM_RULE_ACTION.SCRIPT) return true

        var matched= if(rule.regexPattern) {
            if(rule.ruleValue.isEmpty())
                false
            else checkConstant?.contains(Regex(rule.ruleValue))?:false
        } else
            checkConstant?.contains(rule.ruleValue)?:false

        if(!matched && rule.adClick && rule.style == FEED_ITEM_RULE_SOURCE.IMAGE_URL)
            matched = if(rule.regexPattern) feedItem.description.contains(Regex(rule.ruleValue)) else feedItem.description.contains(rule.ruleValue)

        return  matched
    }

    private fun ruleReplace(src:String,repStr:String,newStr:String,reg:Boolean=false):String{
        return if(reg) src.replace(Regex(repStr),newStr) else src.replace(repStr,newStr)
    }

    private fun ruleReplace(src:String,rule: FeedRule)=ruleReplace(src,rule.ruleValue,rule.replaceValue,rule.regexPattern)
}